Making Of: Managed Plug-In For Snippet Manager
Snippet Manager is an application built by Danny Battison. Its main purpose is to help developers organize their code snippets in the cloud (those aren’t stored locally). One of the interesting features of the application is that it offers an interface for plug-ins, although with a specific code structure.
Here is a sample plug-in developed in C++ that would work with Snippet Manager:
#include <windows.h>
#ifdef __cplusplus
extern "C"
{
#endif
struct snippet {
char* id;
char* title;
char* content;
char* author;
char* language;
bool readonly;
};
// entry point to the program. Consider this as main()
// uid = user id
// NOTE: some members of the snippet may be blank if the snippet is only stored locally!
void __declspec(dllexport) SnippetManagerPluginMain(const LPCSTR uid, snippet s) {
// messagebox to show s.title, the title of the snippet in theοΏ½
// CURRENTLY ACTIVE tab.
// note that if the tab page itself has a number appended to it
// (new snippet | new snippet 2 | new snippet 3 | etc)
// it will still simply be called "new snippet"
MessageBoxA(0, s.title, "Hello!", MB_OK | MB_ICONINFORMATION);
}
// if you don't include this, your plugin simply won't have a keyboard shortcut
char* __declspec(dllexport) SnippetManagerKeyboardShortcut(void) {
return "F5";
}
#ifdef __cplusplus
}
#endif
This plug-in doesn’t include any functionality that would be useful β it just shows a message. But it works and basically shows that the main application accepts external code.
Not every developer wants to work with native C++ to develop a plug-in. Even with managed C++ it wouldn’t be possible to create a plug-in, because Snippet Manager isn’t relying on namespaces, and that’s what managed code is about. Therefore, there is a way to develop a plug-in in managed code, although with a little bit of work.
For my example, I will be using Visual Studio 2008.
First of all, I started Visual Studio and created a new project that is a C# Class Library. I worked specifically with C#, since this is my main language in a managed environment.
Now, I can develop whatever I want here β add a Windows Form, use various libraries and classes (although I need to make sure to bundle them with the plug-in as it will require those to run) β Snippet Manager doesn’t put limits on what a plug-in can do. I developed a Twitter plug-in. All it does is it opens a dialog and allows the user to send a tweet. Here is a screenshot:
Image lost since transition to new blog
To be able to interact with unmanaged code in this case, I needed to create a function, that will trigger a specific action (it can be with parameters or without them) and an interface.
For my main class, the code looked like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TweetIt
{
public interface ITweet
{
void ShowWindow();
}
public class TwitterInterface : ITweet
{
public void ShowWindow()
{
Main main = new Main();
main.ShowDialog();
}
}
}
Main is a Windows Form that is the dialog you’ve seen in the previous screenshot.
This is not all I had to do. I needed to edit the AssemblyInfo.cs to make sure that the library will be COM-accessible, so I added the following lines to the file contents:
[assembly: ComVisible(true)]
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("..\\..\\TweetIt.SNK")]
There is a reference to an assembly key file (Strong Name Key). To get one, I used the sn
tool, that is bundled with .NET Framework.
From the start menu, I selected Visual Studio 2008, then Visual Studio Tools and finally β Visual Studio 2008 Command Prompt.
The following command was used to generate the key:
sn.exe -k TweetIt.SNK
Obviously, the name can be changed, the only requirement being the correct reference in the assembly information file.
I copied the file to the project folder (as you see from the path to the key file, it is located two directory levels below the application path). If I would place it somewhere else, once again, I need to make sure I reference it properly.
After all this, I built the library and now I had a ready-to-go managed DLL in the project bin folder.
I needed to register the DLL and get a TLB file (which is a Type Library file) that will be referenced in the C++ library. To do this, I once again ran the Visual Studio 2008 Command Prompt and used the following command:
RegAsm.exeTweetIt.DLL /tlb:TweetIt.tlb /codebase
All set, and all that had to be done now is create a C++ library that will access the managed plug-in. To do this, I created a Win32 Application that is a blank project, but set to compile as a DLL. Then, I created a blank C++ source file and added the standard plug-in template:
#include <windows.h>
#import "D:\TweetIt.tlb"
#ifdef __cplusplus
extern "C"
{
#endif
struct snippet {
char* id;
char* title;
char* content;
char* author;
char* language;
bool readonly;
};
// entry point to the program. Consider this as main()
// uid = user id
// NOTE: some members of the snippet may be blank if the snippet is only stored locally!
void __declspec(dllexport) SnippetManagerPluginMain(const LPCSTR uid, snippet s) {
}
#ifdef __cplusplus
}
#endif
Notice the fact, that I added a reference to the type library I just created. Now, I need to add the actual functionality, so I put this code inside the main function:
HRESULT hr = CoInitialize(NULL);
ITweetPtr pITweet(_uuidof(TwitterInterface));
pITweet->ShowWindow();
CoUninitialize();
If the function would have a return value or specific parameters, I am able to add those here as well, but my plug-in isn’t using them at the moment. Once I compiled the DLL (C++ version), I copied it to the plugins folder in the main Snippet Manager directory, and the new plug-in is listed in the menu:
Image lost since transition to new blog
That’s all that has to be done to make a managed plug-in work with Snippet Manager. It will be a bit harder to make this kind of plug-ins portable, as the user needs two DLLs instead of one (in case the plug-in was created in native C++) and the managed DLL needs to be registered. But as for experimental purposes, it is something quite interesting to implement.
I used this article to learn how to make managed code interact with native, so it isn’t that hard to implement as someone might think.